#ifndef DIRKN_EXPR_HPP
#define DIRKN_EXPR_HPP

#include <functional>
#include <cassert>
#include <cstdlib>
#include <type_traits>
#include "matrix.hpp"

namespace tws {

    template <typename Op, typename Left, typename Right>
    struct BinX
    {
        //typedef typename Right::value_type value_type;
        typedef typename std::common_type<typename Left::value_type,typename Right::value_type>::type value_type;
        const Left& left;
        const Right& right;
        BinX(const Left& left, const Right& right)
        : left(left), right(right)
        {
            assert((left.rows() == right.rows()) && (left.cols() == right.cols()));
        }
        value_type operator()(std::size_t i, std::size_t j) const
        {
            return Op()(left(i,j), right(i,j));
        }
        std::size_t rows() const
        {
            return right.rows();
        }
        std::size_t cols() const
        {
            return right.cols();
        }
    };

	template <typename Left, class Shape>
    BinX< std::multiplies<typename std::common_type<typename Left::value_type,typename Shape::value_type>::type>, Left, Matrix<Shape> > 
    operator*(const Left& left, const Matrix<Shape>& right)
    {
        return BinX<std::multiplies<typename std::common_type<typename Left::value_type,typename Shape::value_type>::type>, Left, Matrix<Shape>>(left, right);
    }

    template <typename Left, class Shape>
    BinX< std::minus<typename std::common_type<typename Left::value_type,typename Shape::value_type>::type>, Left, Matrix<Shape> > 
    operator-(const Left& left, const Matrix<Shape>& right)
    {
        return BinX<std::minus<typename std::common_type<typename Left::value_type,typename Shape::value_type>::type>, Left, Matrix<Shape>>(left, right);
    }
    
    template <typename Left, class Shape>
    BinX< std::plus<typename std::common_type<typename Left::value_type,typename Shape::value_type>::type>, Left, Matrix<Shape> > 
    operator+(const Left& left, const Matrix<Shape>& right)
    {
        return BinX<std::plus<typename std::common_type<typename Left::value_type,typename Shape::value_type>::type>, Left, Matrix<Shape>>(left, right);
    }

}

#endif
